package mrusanov.player

import mrusanov.terrain._
import util.Random

object PlayerTypeGenerator {
  val MIN_AGILITY = 160
  val MAX_AGILITY = 560

  val MAX_TYPE = 70
  val MIN_TYPE = 20
  val TYPE_STEP = 10

  private[this] val PLAYER_STRENGTH_CONSTANT = 500.0 * (MIN_AGILITY + MAX_AGILITY) / 2

  private lazy val playerTypesList = PlayerTypeInfoLoader.types.values.map(generatePlayerType)
  lazy val playerTypes = playerTypesList.map(pt => pt.name -> pt).toMap


  def generatePlayerType(info:PlayerTypeInfo):PlayerType = {
    val terrainTypesDefence = generateTerrainTypesDefence(info)
    val hpAttack = generateHPAndAttack(info)

    // TODO add name and image path
    new PlayerType(info.name, info.view, hpAttack._1, hpAttack._2, terrainTypesDefence, info.damageType,
      new DamageTypeResistance(info.resistances))
  }

  private def hpResistanceCorrection(resistances:Map[DamageType, Int]):Double = {
    val ideal = DamageType.resistancesCount * 100
    val actual = ideal - resistances.values.reduce(_ + _)
    actual / ideal.toDouble
  }

  private [this] def generateHPAndAttack(info:PlayerTypeInfo):(Double, Double) = {
    val strength = PLAYER_STRENGTH_CONSTANT   / info.agility
    val hp = scala.math.sqrt(info.hpToDamage * strength) * hpResistanceCorrection(info.resistances)
    val damage = scala.math.sqrt(strength / info.hpToDamage)

    (hp, damage)
  }


  private [this] def generateTerrainTypesDefence(info:PlayerTypeInfo):Map[TerrainType, Int] = {
    var combinations = getPermutationsGenerator(info)

    //filter where sum is not equals agility
    combinations = combinations filter(_.reduceLeft(_ + _) == info.agility)

     // filter whether bounded terrain types are counted
    combinations = combinations filter (boundedTypesDefencesAreCorrect(_))

    val result = combinations.toList
    (TerrainType.all zip result(0)).toMap
  }

  private [this] def favoritesAreCounted(defences:List[Int], info:PlayerTypeInfo):Boolean = {
    val terrain = TerrainType.createDefenceMap(defences)
    terrain foreach({ t =>
      if (info.favoriteTypes.contains(t._1) && t._2 != MAX_TYPE) {
        return false
      }

      if (info.hatedTypes.contains(t._1) && t._2 != MIN_TYPE) {
        return false
      }
    })

    true
  }

  private [this] def boundedTypesDefencesAreCorrect(defences:List[Int]):Boolean = {
    val typesAndDefences:Map[TerrainType, Int] = (TerrainType.all zip defences).toMap
    typesAndDefences forall({kv =>
      val currentDefence = kv._2
      val bound = TerrainType.boundTypes(kv._1)

      bound forall({ bnd =>
        val boundDefence = typesAndDefences(bnd)
        scala.math.abs(currentDefence - boundDefence) <= TYPE_STEP
      })
    })
  }

  private [this] def getPermutationsGenerator(info:PlayerTypeInfo):Iterator[List[Int]] = {
    for { a <- getIterator(info, TerrainType.all(0))
          b <- getIterator(info, TerrainType.all(1))
          c <- getIterator(info, TerrainType.all(2))
          d <- getIterator(info, TerrainType.all(3))
          e <- getIterator(info, TerrainType.all(4))
          f <- getIterator(info, TerrainType.all(5))
          g <- getIterator(info, TerrainType.all(6))
          h <- getIterator(info, TerrainType.all(7))
    } yield List(a, b, c, d, e, f, g, h)
  }

  private [this] def getIterator(info:PlayerTypeInfo, ter:TerrainType):Iterator[Int] = {
    if (info.favoriteTypes.contains(ter)) {
      return List(MAX_TYPE).toIterator
    }

    if (info.hatedTypes.contains(ter)) {
      return List(MIN_TYPE).toIterator
    }

    MIN_TYPE.to(MAX_TYPE, TYPE_STEP).toIterator
  }

}
